package org.example.config;

import org.example.filter.JwtFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.annotation.Resource;

/**
 * 1.该文件是 Spring Security的核心配置，它需要去继承一个类 WebSecurityConfigurerAdapter
 * 2.开启spring Security的配置，覆盖 spring security的默认配置, 在该配置类头顶加上一个 @EnableWebSecurity
 *   这样一个注解。
 * 3.要覆盖 spring security的默认配置，需要去重写 configure(HttpSecurity http) 该方法。
 *
 * 4.@EnableGlobalMethodSecurity(prePostEnabled = true)  该注解的作用是让  @PreAuthorize 这个注解生效
 */
@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Resource
    private JwtFilter jwtFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /**
         * 1.authorizeRequests() 表示认证请求，调用了该方法之后，就开始配置接口是否需要认证才能访问，
         *   单独调用该方法意义不大，关键看该方法后续的一些处理。
         * 2.antMatchers() 填写一些路径，然后说明该路径是需要登录才能访问或者不需要的登录直接访问。
         *    1) antMatchers("/greet").permitAll() 表示访问 /greet 这个路径的时候，不需要登录;
         *       permitAll() 表示可以直接访问，不需要登录。
         *    2) antMatchers("/user/**").authenticated() 表示访问以 /user开头的所有的路径，
         *     都必须要登录才能访问；authenticated() 表示认证，也就是说必须要经过认证才能访问对应的接口。
         * 3.在该配置文件中，如果一旦调用 authorizeRequests() 这个方法，会将spring security内置的
         *   登录表单禁用掉；如果需要开启默认的登录页面，可以开启。
         * 4. and() 方法在spring security的代码中，表示一块配置与另外一块配置的分隔，必须要加。
         */
        /**
        http.authorizeRequests()
                .antMatchers("/greet").permitAll()
                .antMatchers("/user/**").authenticated()
                .and()
                .formLogin();  // 表示开启 spring security默认的表单
//                .formLogin().disable(); // 表示将spring security默认的登录表单禁用掉
         */
        /**
         * 1. addFilterBefore() 接受两个参数，第一个参数是要添加的过滤器对象； 第二个参数是将我们自己
         * 的过滤器加到哪个过滤器前面。
         * 2. 将我们自己的过滤器加入到spring security过滤链中，成为整个链的一个环节。
         * 3. UsernamePasswordAuthenticationFilter 是spring security过滤器链中的一个其中一个过滤器。
         * 4. 因为spring security的过滤器链中有很多过滤器，大家知道名字就这个一个。
         */
        http.cors().configurationSource(corsConfigurationSource())  // spring security解决内部过滤器链跨域问题
                .and()
                .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
                .authorizeRequests()
                .antMatchers("/login").permitAll() // 登录需要放其过去
                .antMatchers("/**").authenticated() // 其他的请求都需要登录才能访问
                .and() // and() 表示另外一块的配置
                .csrf().disable(); // 禁用跨站伪造攻击的验证。
    }


    /**
     * 在spring security的过滤器链中，如果去获取请求头的敏感信息，spring security针对这种也不允许访问。
     */
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfigurationSource source =   new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*");    //同源配置，*表示任何请求都视为同源，若需指定ip和端口可以改为如“localhost：8080”，多个以“，”分隔；
        corsConfiguration.addAllowedHeader("*");    //header，允许哪些header，本案中使用的是token，此处可将*替换为token；
        corsConfiguration.addAllowedMethod("*");    //允许的请求方法，PSOT、GET等
        ((UrlBasedCorsConfigurationSource) source).registerCorsConfiguration("/**",corsConfiguration); //配置允许跨域访问的url
        return source;
    }
}
